package cn.com.device.monitor.service;

import cn.com.device.monitor.vo.MqttMessgeVO;
import org.apache.iotdb.isession.SessionDataSet;
import org.apache.iotdb.rpc.IoTDBConnectionException;
import org.apache.iotdb.rpc.StatementExecutionException;
import org.apache.iotdb.session.Session;
import org.apache.tsfile.read.common.RowRecord;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service
public class IotdbService extends BaseQueryService{

    @Value("${iotdb.device.root}")
    private String rootNode;

    /**
     * 保存设备监控数据
     * @param entity
     * @throws IllegalAccessException
     * @throws IoTDBConnectionException
     * @throws StatementExecutionException
     */
    public void save(MqttMessgeVO entity) throws IllegalAccessException, IoTDBConnectionException, StatementExecutionException {
        long timestamp = System.currentTimeMillis();
        String deviceId = entity.getDeviceId();
        List<String> measurements = new ArrayList<>();
        List<String> values = new ArrayList<>();
        nodeAndValuesHandle(entity,measurements,values);
        // 将设备放入根节点以上
        deviceId = rootNode+"."+deviceId;
        this.session.insertRecord(deviceId, timestamp, measurements, values);
    }

    private void nodeAndValuesHandle(MqttMessgeVO entity,List<String> nodes, List<String> values) throws IllegalAccessException {
        Field[] fields = entity.getClass().getDeclaredFields();
        for(Field field: fields){
            field.setAccessible(true);
            String fielName = field.getName();
            if("deviceId".equals(fielName)){
                continue;
            }
            nodes.add(fielName);
            Object value =  field.get(entity);
            values.add(String.valueOf(value));
        }
    }


    public List<MqttMessgeVO> queryData(String deviceId) throws Exception {
        // 将设备放入根节点以上
        deviceId = rootNode+"."+deviceId;
        String sql = "select warn,temperature,status from "+deviceId;
        return this.baseQuery(sql,deviceId);
    }
    public  List<MqttMessgeVO> queryLastData() throws Exception {
        List<MqttMessgeVO> result = new ArrayList<>();
        // 获取所有设备及其节点
        Map<String, List<String>> deviceToMeasurements = getAllDevicesAndMeasurements(session);
        // 获取每个设备中每个节点的最新值
        for (Map.Entry<String, List<String>> entry : deviceToMeasurements.entrySet()) {
            String device = entry.getKey();
            List<String> measurements = entry.getValue();
            StringBuilder sqlBuilder = new StringBuilder("SELECT ");
            for (int i = 0; i < measurements.size(); i++) {
                sqlBuilder.append(measurements.get(i));
                if (i < measurements.size() - 1) {
                    sqlBuilder.append(", ");
                }
            }
            sqlBuilder.append(" FROM ").append(device).append(" ORDER BY time DESC LIMIT 1");
            String sql = sqlBuilder.toString();
            List<MqttMessgeVO> records = this.baseQuery(sql,device);
            result.addAll(records);
        }
        return result;
    }

    private static Map<String, List<String>> getAllDevicesAndMeasurements(Session session) throws Exception {
        Map<String, List<String>> deviceToMeasurements = new HashMap<>();
        // 获取所有设备
        SessionDataSet devicesDataSet = session.executeQueryStatement("SHOW DEVICES");
        while (devicesDataSet.hasNext()) {
            RowRecord record = devicesDataSet.next();
            String device = record.getFields().get(0).getStringValue();
            SessionDataSet measurementsDataSet = session.executeQueryStatement("SHOW TIMESERIES " + device + ".*");
            List<String> measurementNames = new ArrayList<>();
            while (measurementsDataSet.hasNext()) {
                RowRecord measurementRecord = measurementsDataSet.next();
                String measurement = measurementRecord.getFields().get(0).getStringValue();
                String[] parts = measurement.split("\\.");
                measurementNames.add(parts[parts.length - 1]);
            }
            deviceToMeasurements.put(device, measurementNames);
        }
        return deviceToMeasurements;
    }
}
